#include "StdAfx.h"
#include "NetCVars.h"
#include "Lobby/CryLobby.h"
#include "Lobby/CryMatchMaking.h"

#if USE_CRYLOBBY_GAMESPY
#include "Lobby/GameSpy/CryGameSpyLobby.h"
#include "Lobby/GameSpy/CryGameSpyMatchMaking.h"

#define GAMESPY_GAME_DEFAULT_PORT				(64100)
#define GAMESPY_DEFAULT_CONNECTION_TIMEOUT	(60000) // Milliseconds
#define GAMESPY_DEFAULT_SYMMETRICAL_FRIENDS	(TRUE)
#define GAMESPY_DEFAULT_CHECK_CDKEY			(TRUE)
#define GAMESPY_DEFAULT_SESSION_PING		(FALSE)

#endif

#define LOBBY_DEFAULT_PORT							(64090)

CNetCVars * CNetCVars::s_pThis;

CNetCVars::CNetCVars()
{
	memset(this, 0, sizeof(*this));

	NET_ASSERT(!s_pThis);
	s_pThis = this;

	IConsole * c = gEnv->pConsole;

#if ALLOW_ENCRYPTION
#if defined(_DEBUG) || defined(DEBUG)
	REGISTER_COMMAND("net_ExpKeyXch_test", CExponentialKeyExchange::Test,0,"");
#endif
#endif

	REGISTER_CVAR2("cl_tokenid", &TokenId, 0, 0, "Token id expected from client player during connection");

//	REGISTER_CVAR2( "sys_network_CPU", &CPU, 1, 0, "Run network multithreaded" );
	pSchedulerDebug = REGISTER_STRING( "net_scheduler_debug", "0", 0, "Show scheduler debugger for some channel" );
	REGISTER_CVAR2( "net_log", &LogLevel, 0, 0, "Logging level of network system" );

#if ENABLE_DEBUG_KIT
	REGISTER_CVAR2( "net_inspector", &NetInspector, 0, 0, "Logging level of network system" );
	REGISTER_CVAR2( "net_meminfo", &MemInfo, 0, 0, "Display memory usage information" );
	REGISTER_CVAR2( "net_logcomments", &LogComments, 0, 0, "Enable ultra verbose logging" );
	REGISTER_CVAR2( "net_showobjlocks", &ShowObjLocks, 0, 0, "show lock debugging display");
	REGISTER_CVAR2( "net_pq_log", &EndpointPendingQueueLogging, 0, 0, "Log updates to the pending queue for each channel");
	REGISTER_CVAR2( "net_disconnect_on_uncollected_breakage", &DisconnectOnUncollectedBreakage, 0, 0, "Disconnect on uncollected breakage" );
	REGISTER_CVAR2( "net_debug_connection_state", &DebugConnectionState, 0, 0, "Show current connecting status as a head-up display" );
	REGISTER_CVAR2( "net_perfcounters", &PerfCounters, 0, 0, "Display some dedicated server performance counters" );
	REGISTER_CVAR2( "net_gamespy_debug",&GSDebugOutput,0,VF_NULL,"Sets debug output level for GameSpy SDKs");
	REGISTER_CVAR2( "net_random_packet_corruption", &RandomPacketCorruption, 0, VF_CHEAT, "" );
#endif

#if LOG_MESSAGE_DROPS
	REGISTER_CVAR2( "net_log_dropped_messages", &LogDroppedMessagesVar, 0, 0, "Log debug information for dropped messages" );
#endif
	REGISTER_CVAR2( "net_channelstats", &ChannelStats, 0, 0, "Display bandwidth statistics per-channel" );
	REGISTER_CVAR2( "net_packetsendrate", &PacketSendRate, 10, 0, "per channel packet send rate (packets per second).  sends whenever possible if this is set to zero." );
	REGISTER_CVAR2( "net_bw_aggressiveness", &BandwidthAggressiveness, 0.5f, 0, "Balances TCP friendlyness versus prioritization of game traffic");
#ifdef _RELEASE
	REGISTER_CVAR2( "net_inactivitytimeout", &InactivityTimeout, 10.f, 0, "Sets how many seconds without receiving a packet a connection will stay alive for (can only be set on server)" );
	REGISTER_CVAR2( "net_inactivitytimeoutDevmode", &InactivityTimeoutDevmode, 10.f, 0, "Sets how many seconds without receiving a packet a connection will stay alive for while in devmode (can only be set on server)" );
#else
	REGISTER_CVAR2( "net_inactivitytimeout", &InactivityTimeout, 30.f, 0, "Sets how many seconds without receiving a packet a connection will stay alive for (can only be set on server)" );
	REGISTER_CVAR2( "net_inactivitytimeoutDevmode", &InactivityTimeoutDevmode, 30.f, 0, "Sets how many seconds without receiving a packet a connection will stay alive for while in devmode (can only be set on server)" );
#endif
	REGISTER_CVAR2( "net_minMicrophoneNotificationInterval", &minMicrophonNotificationInterval, 1.f, 0, "minimum interval between microphone status notification send/requests" );
	REGISTER_CVAR2( "net_serverpingnotificationinterval", &serverPingNotificationInterval, 10.0f, 0, "minimum interval between server ping notification sends" );
	REGISTER_CVAR2( "net_backofftimeout", &BackoffTimeout, 6*60, 0, "Maximum time to allow a remote machine to stall for before disconnecting" );
	REGISTER_CVAR2( "sv_maxmemoryusage", &MaxMemoryUsage, 0, 0, "Maximum memory a dedicated server is allowed to use" );


	REGISTER_CVAR2( "net_new_queue_behaviour", &NewQueueBehaviour, 1, 0, "When set to 1, this will try to minimize the number of packets sent on a channel." );
	REGISTER_CVAR2( "net_safetysleeps", &SafetySleeps, 0, 0, "Controls whether the network layer will perform safety sleeps between context establishment tasks." );
	REGISTER_CVAR2( "net_maxpacketsize", &MaxPacketSize, 1000, 0, "Sets the maximum packet size we will attempt to send (not including UDP/other headers)" );
	REGISTER_CVAR2( "net_ping_time", &PingTime, 5.f, 0, "Controls the length of time between ping packets." );
	REGISTER_CVAR2( "net_keepalive_time", &KeepAliveTime, 10.f, 0, "Controls the length of time between keepalive packets." );





















#if NET_ASSERT_LOGGING
	REGISTER_CVAR2( "net_assertlogging", &AssertLogging, 0, VF_DUMPTODISK, "Log network assertations" );
#endif

#if LOG_INCOMING_MESSAGES || LOG_OUTGOING_MESSAGES
	REGISTER_CVAR2( "net_logmessages", &LogNetMessages, 0, VF_NULL,"" );
#endif
#if LOG_BUFFER_UPDATES
	REGISTER_CVAR2( "net_logbuffers", &LogBufferUpdates, 0, VF_NULL,"" );
#endif
#if STATS_COLLECTOR_INTERACTIVE
	REGISTER_CVAR2( "net_showdatabits", &ShowDataBits, 0, 0, "show bits used for different data" );
#endif
#if ENABLE_DEBUG_KIT
	static const int DEFAULT_CHEAT_PROTECTION = 3;
#else
	static const int DEFAULT_CHEAT_PROTECTION = 0;//0 for GameK01 for now
#endif
	REGISTER_CVAR2( "sv_cheatprotection", &CheatProtectionLevel, DEFAULT_CHEAT_PROTECTION, 0, "" );
	pVoiceCodec=REGISTER_STRING( "sv_voicecodec", "speex", VF_REQUIRE_LEVEL_RELOAD,"" );
	REGISTER_CVAR2("sv_voice_enable_groups",&EnableVoiceGroups,1,VF_NULL,"");

	REGISTER_CVAR2("net_enable_voice_chat",&EnableVoiceChat,1,VF_REQUIRE_APP_RESTART,"");
	REGISTER_CVAR2("net_rtt_convergence_factor", &RTTConverge, 995,VF_NULL,"");
#if !ALWAYS_CHECK_CD_KEYS
  REGISTER_CVAR2( "sv_check_cd_keys",&CheckCDKeys, 0, VF_NULL, "Sets CDKey validation mode:\n 0 - no check\n 1 - check&disconnect unverified\n 2 - check only\n");
#endif
  REGISTER_CVAR2( "sv_ranked", &RankedServer, 0, VF_NULL, "Enable statistics report, for official servers only.");
	// network visualizationsv_
#if ENABLE_DEBUG_KIT
	REGISTER_CVAR2( "net_vis_mode", &VisMode, 0,VF_NULL,"" );
	REGISTER_CVAR2( "net_vis_window", &VisWindow, 0/*.3f*/,VF_NULL,"" );
	REGISTER_CVAR2( "net_show_ping", &ShowPing, 0,VF_NULL,"" );
#endif

#if INTERNET_SIMULATOR
	REGISTER_CVAR2( "net_packetlossrate", &PacketLossRate, 0.0f, VF_CHEAT, "")->SetOnChangeCallback(OnPacketLossRateChange );
	REGISTER_CVAR2( "net_packetextralag", &PacketExtraLag, 0.0f, VF_CHEAT, "")->SetOnChangeCallback(OnPacketExtraLagChange );
#endif

	REGISTER_CVAR2( "net_highlatencythreshold", &HighLatencyThreshold, 0.5f, VF_NULL, "" ); // must be net synched
	REGISTER_CVAR2( "net_highlatencytimelimit", &HighLatencyTimeLimit, 1.5f, VF_NULL, "" ); // must be net synched

	REGISTER_CVAR2( "net_remotetimeestimationwarning", &RemoteTimeEstimationWarning, 0, VF_NULL, "" ); 

	REGISTER_CVAR2( "net_enable_tfrc", &EnableTFRC, 1,VF_NULL,"" );

	//REGISTER_CVAR2( "net_wmi_check_interval", &WMICheckInterval, gEnv->IsDedicated() ? 0.0f : 2.0f, 0 );
	REGISTER_CVAR2("net_connectivity_detection_interval", &NetworkConnectivityDetectionInterval, 1.0f,VF_NULL,"");

#if ENABLE_UDP_PACKET_FRAGMENTATION
	REGISTER_CVAR2("net_packetfragmentlossrate", &PacketFragmentLossRate, 0.0f, VF_CHEAT, "")->SetOnChangeCallback(OnPacketFragmentLossRateChange);	
	REGISTER_CVAR2("net_max_fragmented_packets_per_source", &net_max_fragmented_packets_per_source, 2, VF_NULL, "Maximum number of fragemented packets in flight per source");
	REGISTER_CVAR2("net_fragment_expiration_time", &net_fragment_expiration_time, 1.0f, VF_NULL, "Expiration time for stale fragmented packets");
#endif // ENABLE_UDP_PACKET_FRAGMENTATION

	// TODO: remove/cleanup
	REGISTER_FLOAT( "net_defaultChannelBitRateDesired", 200000.0f, VF_READONLY,"" );
	REGISTER_FLOAT( "net_defaultChannelBitRateToleranceLow", 0.5f, VF_READONLY,"" );
	REGISTER_FLOAT( "net_defaultChannelBitRateToleranceHigh", 0.001f, VF_READONLY,"" );
	REGISTER_FLOAT( "net_defaultChannelPacketRateDesired", 50.0f, VF_READONLY,"" );
	REGISTER_FLOAT( "net_defaultChannelPacketRateToleranceLow", 0.1f, VF_READONLY,"" );
	REGISTER_FLOAT( "net_defaultChannelPacketRateToleranceHigh", 2.0f, VF_READONLY,"" );
	REGISTER_FLOAT( "net_defaultChannelIdlePacketRateDesired", 0.05f, VF_READONLY,"" );

  REGISTER_CVAR2("net_voice_lead_packets", &VoiceLeadPackets, 5,VF_NULL,"" );
  REGISTER_CVAR2( "net_voice_trail_packets", &VoiceTrailPackets, 5,VF_NULL,"" );
	REGISTER_CVAR2( "net_voice_proximity", &VoiceProximity, 0.0f, VF_NULL,"");

  REGISTER_CVAR2( "net_lan_scanport_first", &LanScanPortFirst,  64087, VF_DUMPTODISK, "Starting port for LAN games scanning");
  REGISTER_CVAR2( "net_lan_scanport_num", &LanScanPortNum, 5, VF_DUMPTODISK, "Num ports for LAN games scanning" );

#if ENABLE_DEBUG_KIT
	REGISTER_COMMAND("net_reload_scheduler", ReloadScheduler, 0, "Reload game/scripts/network/scheduler.xml");
#endif

  REGISTER_COMMAND("net_set_cdkey", SetCDKey,VF_DUMPTODISK,"");
	StatsLogin = REGISTER_STRING( "net_stats_login", "", VF_DUMPTODISK, "Login for reporting stats on dedicated server");
	StatsPassword = REGISTER_STRING( "net_stats_pass", "", VF_DUMPTODISK, "Password for reporting stats on dedicated server");

	REGISTER_COMMAND("net_dump_object_state", DumpObjectState, 0,"");

#if NETWORK_REBROADCASTER
	REGISTER_COMMAND("net_rebroadcaster", RebroadcasterCmd, 0, "net_rebroadcaster on/off - enables/disables the rebroadcaster\n"
																														 "net_rebroadcaster debug on/off - enables/disables debug info\n"
																														 "net_rebroadcaster <from channel id> <to channel id> on/off - enables/disables individual connections\n"
																														 "net_rebroadcaster - displays status");
	REGISTER_INT("net_enable_rebroadcaster", 1, VF_DUMPTODISK, "enables/disables rebroadcaster");
#endif
#if NETWORK_HOST_MIGRATION




#define MIGRATION_CRASH_DETECTOR_FUDGE_FACTOR		(0.f)


#ifdef _RELEASE
	REGISTER_CVAR2( "net_migrate_timeout", &hostMigrationTimeout, 30.0f+MIGRATION_CRASH_DETECTOR_FUDGE_FACTOR, VF_DUMPTODISK, "timeout for host migration failure" );
#else
	REGISTER_CVAR2( "net_migrate_timeout", &hostMigrationTimeout, 45.0f+MIGRATION_CRASH_DETECTOR_FUDGE_FACTOR, VF_DUMPTODISK, "timeout for host migration failure" );
#endif
	REGISTER_CVAR2( "net_automigrate_host", &netAutoMigrateHost, 1, VF_DUMPTODISK, "Toggles whether server migration is automatically attempted" );
	REGISTER_CVAR2( "net_show_new_host_hinting", &showNewHostHinting, 0, VF_DUMPTODISK, "Toggles whether new host hinting displayed on screen" );
	REGISTER_CVAR2( "net_show_host_identification", &showHostIdentification, 0, VF_DUMPTODISK, "Toggles whether host identification is displayed on screen" );
	REGISTER_CVAR2( "net_hostHintingNATTypeOverride", &netHostHintingNATTypeOverride, 0, VF_DUMPTODISK, "Override the reported NAT type" );
	REGISTER_CVAR2( "net_hostHintingActiveConnectionsOverride", &netHostHintingActiveConnectionsOverride, 0, VF_DUMPTODISK, "Override the reported number of active connections" );
	REGISTER_CVAR2( "net_hostHintingPingOverride", &netHostHintingPingOverride, 0, VF_DUMPTODISK, "Override the reported ping for all active connections" );
	REGISTER_CVAR2( "net_pruneConnectionsAfterHostMigration", &netPruneConnectionsAfterHostMigration, 1, VF_DUMPTODISK, "Prune connections from players who fail to migrate" );
	REGISTER_COMMAND( "net_ensure_best_host_for_session", EnsureBestHostForSessionCmd, 0, "net_ensure_best_host_for_session <session handle>" );
	REGISTER_CVAR2( "net_anticipated_new_host_leaves", &netAnticipatedNewHostLeaves, 0, VF_DUMPTODISK, "If this machine is the anticipated new host, it leaves during host migration" );
	REGISTER_CVAR2( "net_alternate_deferred_disconnect", &netAlternateDeferredDisconnect, 1, VF_DUMPTODISK, "Enables alternate deferred disconnect code" );
#if ENABLE_HOST_MIGRATION_STATE_CHECK
	REGISTER_CVAR2( "net_do_host_migration_state_check", &doHostMigrationStateCheck, 0, VF_DUMPTODISK, "Enables state checking after host migration. " );
#endif
#endif
	REGISTER_CVAR2( "net_show_matchmaking_tasks", &showMatchMakingTasks, 0, VF_DUMPTODISK, "Toggles whether active matchmaking tasks are displayed on screen" );

#if ENABLE_DEBUG_KIT
	REGISTER_COMMAND("net_stall", Stall, VF_CHEAT, "stall the network thread for a time (default 1 second, but can be passed in as a parameter");
#endif
	
	REGISTER_CVAR2("net_lobby_default_port", &lobbyDefaultPort,LOBBY_DEFAULT_PORT,VF_DUMPTODISK,"Modify the default port for lobbies (effects LAN Lobby currently)");

#if USE_CRYLOBBY_GAMESPY
	REGISTER_CVAR2("net_lobby_gamespy_online_port", &lobbyGameSpyOnlinePort,GAMESPY_GAME_DEFAULT_PORT,VF_DUMPTODISK,"Modify the online port for GameSpy");
	REGISTER_CVAR2("net_lobby_gamespy_connection_wait_timeout", &connectionWaitTimeOut,GAMESPY_DEFAULT_CONNECTION_TIMEOUT,VF_DUMPTODISK,"Modify the connection wait timeout for GameSpy");
	REGISTER_CVAR2("net_lobby_gamespy_symmetrical_friends", &gamespySymmetricalFriends,GAMESPY_DEFAULT_SYMMETRICAL_FRIENDS,VF_DUMPTODISK,"Toggle whether GameSpy friend relationships are bidirectional");
#ifndef _RELEASE
	REGISTER_CVAR2("net_lobby_gamespy_check_cdkey", &gamespyCheckCDKey,GAMESPY_DEFAULT_CHECK_CDKEY,VF_DUMPTODISK,"Toggle whether CD keys are authenticated");
#endif
#if GAMESPY_ENABLE_SESSION_PING
	REGISTER_CVAR2("net_lobby_gamespy_session_ping", &gamespySessionPing,GAMESPY_DEFAULT_SESSION_PING,VF_DUMPTODISK,"Toggle whether a ping is sent to sessions before returning them to game");
#endif
#endif

	REGISTER_CVAR2("net_minTCPFriendlyBitRate", &MinTCPFriendlyBitRate, 30000.f, VF_NULL, "Minimum bitrate allowed for TCP friendly transmission");

  REGISTER_CVAR2( "net_UseDeprecatedVoiceSystem", &useDeprecatedVoiceSystem, 1, VF_DUMPTODISK, "Use deprecated voice system" );

	NET_PROFILE_REG_CVARS();

#if USE_CRYLOBBY_GAMESPY
	REGISTER_COMMAND( "gamespy_get_profile_id", GameSpyGetProfileIDCmd, VF_CHEAT, "Get GameSpy profile ID for user 0" );
	REGISTER_COMMAND( "gamespy_log_invites", GameSpyLogInvitesCmd, VF_CHEAT, "Log GameSpy invites" );
	REGISTER_COMMAND( "gamespy_send_invite", GameSpySendInviteCmd, VF_CHEAT, "Send a GameSpy invite" );
	REGISTER_COMMAND( "gamespy_accept_invite", GameSpyAcceptInviteCmd, VF_CHEAT, "Accept a GameSpy invite" );
	REGISTER_COMMAND( "gamespy_list_friends", GameSpyListFriendsCmd, VF_CHEAT, "List GameSpy friends" );
	REGISTER_COMMAND( "gamespy_send_friend_request", GameSpySendFriendRequestCmd, VF_CHEAT, "Send a GameSpy friend request" );
	REGISTER_COMMAND( "gamespy_accept_friend_request", GameSpyAcceptFriendRequestCmd, VF_CHEAT, "Accept a GameSpy friend request" );
	REGISTER_COMMAND( "gamespy_revoke_friend_status", GameSpyRevokeFriendStatusCmd, VF_CHEAT, "Revoke GameSpy friend status" );
	REGISTER_COMMAND( "gamespy_log_friend_events", GameSpyLogFriendEventsCmd, VF_CHEAT, "Log GameSpy friend events" );
	REGISTER_COMMAND( "gamespy_block_profile_id", GameSpyBlockProfile, VF_CHEAT, "Block a profile id" );
	REGISTER_COMMAND( "gamespy_unblock_profile_id", GameSpyUnblockProfile, VF_CHEAT, "Unblock a profile id" );
	REGISTER_COMMAND( "gamespy_is_profile_id_blocked", GameSpyIsProfileBlocked, VF_CHEAT, "Checks if a profile is blocked" );
	REGISTER_COMMAND( "gamespy_is_user_friend", GameSpyIsUserFriend, VF_CHEAT, "Checks if a profile is a friend" );
	REGISTER_COMMAND( "gamespy_get_name", GameSpyGetName, VF_CHEAT, "Gets the unique nick of a profile with the given ID" );
	REGISTER_COMMAND( "gamespy_find_user", GameSpyFindUser, VF_CHEAT, "Gets the ID of a profile with the given unique nick" );
#endif

#if ENABLE_CRYLOBBY_DEBUG_TESTS
	REGISTER_CVAR2("cld_enable", &cldEnable, 0, VF_CHEAT, "Enable cry lobby debug tests");
	REGISTER_CVAR2("cld_error_percentage", &cldErrorPercentage, 10, VF_CHEAT, "How often a cry lobby task will return with a time out error");
	REGISTER_CVAR2("cld_min_delay_time", &cldMinDelayTime, 2000, VF_CHEAT, "The minimum time a cry lobby task will be delayed before processing in milliseconds");
	REGISTER_CVAR2("cld_max_delay_time", &cldMaxDelayTime, 5000, VF_CHEAT, "The maximum time a cry lobby task will be delayed before processing in milliseconds");
#endif

#if ENABLE_CORRUPT_PACKET_DUMP
	REGISTER_CVAR2("net_packet_read_debug_output", &packetReadDebugOutput, 0, VF_CHEAT, "Enable debug output when reading a packet");
#endif

	// BREAKAGE
	REGISTER_CVAR2("net_breakage_sync_entities", &breakageSyncEntities, 1, VF_NULL, "Allow net syncing/binding of breakage entities");

#if USE_CRYLOBBY_GAMESPY
	REGISTER_CVAR2( "gamespy_search_ping_retry", &gamespySearchPingRetry, 1000, VF_CHEAT, "GameSpy search ping retry interval in milliseconds" );
	REGISTER_CVAR2( "gamespy_search_ping_timeout", &gamespySearchPingTimeOut, 10000, VF_CHEAT, "GameSpy search ping timeout in milliseconds");
#endif

	REGISTER_CVAR2("net_enable_watchdog_timer", &enableWatchdogTimer, 0, VF_CHEAT, "Enable watchdog timer. Not needed if CryLobby is being used.");
}

CNetCVars::~CNetCVars()
{
	NET_ASSERT(s_pThis);
	s_pThis = 0;
}

void CNetCVars::DumpObjectState(IConsoleCmdArgs* pArgs)
{
	SCOPED_GLOBAL_LOCK;
	CNetwork::Get()->BroadcastNetDump( eNDT_ObjectState );
}

#if ENABLE_DEBUG_KIT
void CNetCVars::ReloadScheduler(IConsoleCmdArgs* pArgs)
{
	SCOPED_GLOBAL_LOCK;
	CNetwork::Get()->ReloadScheduler();
}
#endif

void CNetCVars::SetCDKey( IConsoleCmdArgs* pArgs)
{
  SCOPED_GLOBAL_LOCK;

  if(pArgs->GetArgCount()>1)
  {
    CNetwork::Get()->SetCDKey(pArgs->GetArg(1));
  }
  else
  {
    CNetwork::Get()->SetCDKey("");
  }
}

//------------------------------------------------------------------------
#if NETWORK_REBROADCASTER
void CNetCVars::RebroadcasterCmd(IConsoleCmdArgs* pArgs)
{
	CryFixedStringT<128> temp;
	int argCount = pArgs->GetArgCount();
	CCryRebroadcaster* pRebroadcaster = NULL;
	CCryLobby* pLobby = (CCryLobby*)CCryLobby::GetLobby();
	if (pLobby)
	{
		pRebroadcaster = pLobby->GetRebroadcaster();
	}

	// Console text colouring:
	// $0 = black | $1 = white  | $2 = blue    | $3 = green  | $4 = red
	// $5 = cyan  | $6 = yellow | $7 = magenta | $8 = orange | %9 = grey

	if (argCount == 1)
	{
		// The command with no arguments simply logs the rebroadcaster status
		if (pRebroadcaster)
		{
			pRebroadcaster->ShowMesh();
		}

		CryLog("  $3net_rebroadcaster $2from $5to $6status $1- where status is enable/disable, for a single connection");
	}
	else if (argCount == 2)
	{
		// Enable or disable the rebroadcaster
		bool enable = false;
		temp = pArgs->GetArg(1);

		if (temp.compareNoCase("disable") == 0 || temp.compareNoCase("off") == 0 || temp.compareNoCase("0") == 0 || temp.compareNoCase("n") == 0)
		{
			enable = false;
		}
		else if (temp.compareNoCase("enable") == 0 || temp.compareNoCase("on") == 0 || temp.compareNoCase("1") == 0 || temp.compareNoCase("y") == 0)
		{
			enable = true;
		}

		gEnv->pConsole->GetCVar("net_enable_rebroadcaster")->Set(enable ? 1 : 0);

		temp.Format("CryNetwork rebroadcaster %s", (enable) ? "$3enabled" : "$4disabled");
		CryLog(temp);
	}
	else if (argCount == 3)
	{
		temp = pArgs->GetArg(1);

		if (temp.compareNoCase("debug") == 0)
		{
			// Enable/disable debug info
			temp = pArgs->GetArg(2);
			bool enable = false;

			if (temp.compareNoCase("disable") == 0 || temp.compareNoCase("off") == 0 || temp.compareNoCase("0") == 0 || temp.compareNoCase("n") == 0)
			{
				enable = false;
			}
			else if (temp.compareNoCase("enable") == 0 || temp.compareNoCase("on") == 0 || temp.compareNoCase("1") == 0 || temp.compareNoCase("y") == 0)
			{
				enable = true;
			}

			if (pRebroadcaster)
			{
				pRebroadcaster->DebugMode(enable);
			}

			temp.Format("CryNetwork rebroadcaster debug info is %s", (enable) ? "$3enabled" : "$4disabled");
			CryLog(temp);
		}
	}
	else if (argCount == 4)
	{
		// Set the status of an individual connection
		temp = pArgs->GetArg(1);
		uint32 fromChannelID = atoi(temp.c_str());
		temp = pArgs->GetArg(2);
		uint32 toChannelID = atoi(temp.c_str());

		ERebroadcasterConnectionStatus status = eRCS_Unknown;
		temp = pArgs->GetArg(3);
		status = eRCS_Unknown;

		if (temp.compareNoCase("disable") == 0 || temp.compareNoCase("off") == 0 || temp.compareNoCase("0") == 0 || temp.compareNoCase("n") == 0)
		{
			status = eRCS_Disabled;
		}
		else if (temp.compareNoCase("enable") == 0 || temp.compareNoCase("on") == 0 || temp.compareNoCase("1") == 0 || temp.compareNoCase("y") == 0)
		{
			status = eRCS_Enabled;
		}

		if (pRebroadcaster)
		{
			pRebroadcaster->SetStatus(fromChannelID, toChannelID, status, true);
		}
	}
}
#endif

#if NETWORK_HOST_MIGRATION
//------------------------------------------------------------------------
void CNetCVars::EnsureBestHostForSessionCmd(IConsoleCmdArgs* pArgs)
{
	int argCount = pArgs->GetArgCount();
	if (argCount == 2)
	{
		CrySessionHandle h = atoi(pArgs->GetArg(1));
		if (h != CrySessionInvalidHandle)
		{
			CCryLobby* pLobby = (CCryLobby*)CCryLobby::GetLobby();
			if (pLobby != NULL)
			{
				CCryMatchMaking* pMatchMaking = (CCryMatchMaking*)pLobby->GetMatchMaking();
				if (pMatchMaking != NULL)
				{
					h = pMatchMaking->CreateGameSessionHandle(h, CryMatchMakingInvalidConnectionUID);
					pMatchMaking->SessionEnsureBestHost(h, NULL, NULL, NULL);
				}
			}
		}
	}
	else
	{
		CryLog("Bad arguments for net_ensure_best_host_for_session");
	}
}
#endif

//------------------------------------------------------------------------
#if ENABLE_DEBUG_KIT
void CNetCVars::Stall( IConsoleCmdArgs* pArgs )
{
	float stallTime = 1.0f;
	if (pArgs && pArgs->GetArgCount()>=2)
	{
		if (!sscanf(pArgs->GetArg(1), "%f", &stallTime))
			stallTime = 1.0f;
	}
	if (stallTime > 0.0f)
	{
		NetLogAlways("Stalling all sending for %f seconds", stallTime);
		Get().StallEndTime = g_time + stallTime;
	}
	else
	{
		NetLogAlways("Sleeping for %f seconds", -stallTime);
		SCOPED_GLOBAL_LOCK;
		Sleep((DWORD)(-1000.0f*stallTime));
	}
}
#endif


#if USE_CRYLOBBY_GAMESPY

void CNetCVars::GameSpyGetProfileIDCmd( IConsoleCmdArgs* pArgs )
{
	ICryLobbyService*					pLobbyService = gEnv->pNetwork->GetLobby()->GetLobbyService( eCLS_Online );;
	CryUserID									pUserID = pLobbyService->GetUserID( 0 );;
	const SCryGameSpyUserID*	pGameSpyUserID = static_cast< const SCryGameSpyUserID* >( pUserID.get() );;

	NetLogAlways( "[GameSpy] Your GameSpy profile ID is %u", pGameSpyUserID->m_profileID );
}


void CNetCVars::GameSpyAcceptInviteCmd( IConsoleCmdArgs* pArgs )
{
	ICryLobbyService*				pLobbyService;
	ICryFriendsManagement*	pFriendsManagement;

	pLobbyService = gEnv->pNetwork->GetLobby()->GetLobbyService( eCLS_Online );
	pFriendsManagement = pLobbyService->GetFriendsManagement();

	if ( pFriendsManagement )
	{
		const char*						pArg = NULL;
		char*									pEnd;

		if ( pArgs && ( pArgs->GetArgCount() == 2 ) )
		{
			pArg = pArgs->GetArg( 1 );
		}

		if ( pArg )
		{
			GPProfile							profileID = strtoul( pArg, &pEnd, 10 );

			if ( ( pEnd > pArg ) && !*pEnd )
			{
				SCryGameSpyUserID*		pGameSpyUserID = new SCryGameSpyUserID( profileID );

				if ( pGameSpyUserID )
				{
					CryUserID							pUserID = pGameSpyUserID;
					ECryLobbyError				error = pFriendsManagement->FriendsManagementAcceptInvite( 0, &pUserID, NULL, NULL, NULL );

					if ( error == eCLE_Success )
					{
						NetLogAlways( "[GameSpy] Accepted invite from profile ID %u", profileID );
					}
					else
					{
						NetLogAlways( "[GameSpy] Error accepting invite from profile ID %u: error code %u", profileID, error );
					}
				}
				else
				{
					NetLogAlways( "[GameSpy] Error: out of memory" );
				}
			}
			else
			{
				NetLogAlways( "[GameSpy] Error: argument must be a GameSpy profile ID" );
			}
		}
		else
		{
			NetLogAlways( "[GameSpy] Error: command requires exactly one argument" );
		}
	}
	else
	{
		NetLogAlways( "[GameSpy] Error: no friends management" );
	}
}


void CNetCVars::GameSpySendInviteCmd( IConsoleCmdArgs* pArgs )
{
	ICryLobbyService*				pLobbyService;
	CCryGameSpyMatchMaking*	pMatchMaking;
	ICryFriends*						pFriends;

	pLobbyService = gEnv->pNetwork->GetLobby()->GetLobbyService( eCLS_Online );
	pMatchMaking = static_cast< CCryGameSpyMatchMaking* >( pLobbyService->GetMatchMaking() );
	pFriends = pLobbyService->GetFriends();

	if ( pMatchMaking )
	{
		if ( pFriends )
		{
			const char*						pArg = NULL;

			if ( pArgs && ( pArgs->GetArgCount() == 2 ) )
			{
				pArg = pArgs->GetArg( 1 );
			}

			if ( pArg )
			{
				char*									pEnd;
				GPProfile							profileID = strtoul( pArg, &pEnd, 10 );

				if ( ( pEnd > pArg ) && !*pEnd )
				{
					SCryGameSpyUserID*		pGameSpyUserID = new SCryGameSpyUserID( profileID );

					if ( pGameSpyUserID )
					{
						CrySessionHandle			sessionHandle = pMatchMaking->GetCurrentInvitableSessionHandle();

						if ( sessionHandle != CrySessionInvalidHandle )
						{
							CryUserID							pUserID = pGameSpyUserID;
							ECryLobbyError				error = pFriends->FriendsSendGameInvite( 0, sessionHandle, &pUserID, 1, NULL, NULL, NULL );

							if ( error == eCLE_Success )
							{
								NetLogAlways( "[GameSpy] Sent invite to profile ID %u", profileID );
							}
							else
							{
								NetLogAlways( "[GameSpy] Error sending invite to profile ID %u: error code %u", profileID, error );
							}
						}
						else
						{
							NetLogAlways( "[GameSpy] Error: no current invitable session" );
						}
					}
					else
					{
						NetLogAlways( "[GameSpy] Error: out of memory" );
					}
				}
				else
				{
					NetLogAlways( "[GameSpy] Error: argument must be a GameSpy profile ID" );
				}
			}
			else
			{
				NetLogAlways( "[GameSpy] Error: command requires exactly one argument" );
			}
		}
		else
		{
			NetLogAlways( "[GameSpy] Error: no friends" );
		}
	}
	else
	{
		NetLogAlways( "[GameSpy] Error: no matchmaking" );
	}
}


void CNetCVars::GameSpyLogInvitesCmd( IConsoleCmdArgs* pArgs )
{
	gEnv->pNetwork->GetLobby()->RegisterEventInterest( eCLSE_RecievedInvite, GameSpyLogInvitesCallback, NULL );
	NetLogAlways( "[GameSpy] Now logging invites" );
}


void CNetCVars::GameSpyLogInvitesCallback( UCryLobbyEventData eventData, void *pUserParam )
{
	const SCryGameSpyUserID*	pSenderGameSpyUserID = static_cast< const SCryGameSpyUserID* >( eventData.pFriendMesssageData->m_user.get() );
	int32								productID;
	int64								sessionID;

	sscanf_s( eventData.pFriendMesssageData->m_message, "%d %llX", &productID, &sessionID );
	NetLogAlways( "[GameSpy] Invite to product %d, session %llX from user %d", productID, sessionID, pSenderGameSpyUserID->m_profileID );
}


void CNetCVars::GameSpyListFriendsCmd( IConsoleCmdArgs* pArgs )
{
	ICryLobbyService*				pLobbyService;
	ICryFriends*						pFriends;

	pLobbyService = gEnv->pNetwork->GetLobby()->GetLobbyService( eCLS_Online );
	pFriends = pLobbyService->GetFriends();

	if ( pFriends )
	{
		ECryLobbyError				error = pFriends->FriendsGetFriendsList( 0, 0, 0, NULL, GameSpyListFriendsCallback, NULL );

		if ( error != eCLE_Success )
		{
			NetLogAlways( "[GameSpy] Error listing friends: error code %u", error );
		}
	}
	else
	{
		NetLogAlways( "[GameSpy] Error: no friends" );
	}
}


void CNetCVars::GameSpyListFriendsCallback( CryLobbyTaskID taskID, ECryLobbyError error, SFriendInfo* pFriendInfo, uint32 numFriends, void* pArg )
{
	const SCryGameSpyUserID*			pUserID;

	for ( uint32 i = 0; i < numFriends; ++i )
	{
		pUserID = static_cast< const SCryGameSpyUserID* >( pFriendInfo[ i ].userID.get() );
		NetLogAlways( "[GameSpy] Friend: %-*s %u", CRYLOBBY_USER_NAME_LENGTH, pFriendInfo[ i ].name, pUserID->m_profileID );
	}
}


void CNetCVars::GameSpySendFriendRequestCmd( IConsoleCmdArgs* pArgs )
{
	ICryLobbyService*				pLobbyService;
	ICryFriendsManagement*	pFriendsManagement;

	pLobbyService = gEnv->pNetwork->GetLobby()->GetLobbyService( eCLS_Online );
	pFriendsManagement = pLobbyService->GetFriendsManagement();

	if ( pFriendsManagement )
	{
		const char*						pArg = NULL;
		char*									pEnd;

		if ( pArgs && ( pArgs->GetArgCount() == 2 ) )
		{
			pArg = pArgs->GetArg( 1 );
		}

		if ( pArg )
		{
			GPProfile							profileID = strtoul( pArg, &pEnd, 10 );

			if ( ( pEnd > pArg ) && !*pEnd )
			{
				SCryGameSpyUserID*		pGameSpyUserID = new SCryGameSpyUserID( profileID );

				if ( pGameSpyUserID )
				{
					CryUserID							pUserID = pGameSpyUserID;
					ECryLobbyError				error = pFriendsManagement->FriendsManagementSendFriendRequest( 0, &pUserID, 1, NULL, NULL, NULL );

					if ( error == eCLE_Success )
					{
						NetLogAlways( "[GameSpy] Sent friend request to profile ID %u", profileID );
					}
					else
					{
						NetLogAlways( "[GameSpy] Error sending friend request to profile ID %u: error code %u", profileID, error );
					}
				}
				else
				{
					NetLogAlways( "[GameSpy] Error: out of memory" );
				}
			}
			else
			{
				NetLogAlways( "[GameSpy] Error: argument must be a GameSpy profile ID" );
			}
		}
		else
		{
			NetLogAlways( "[GameSpy] Error: command requires exactly one argument" );
		}
	}
	else
	{
		NetLogAlways( "[GameSpy] Error: no friends management" );
	}
}


void CNetCVars::GameSpyAcceptFriendRequestCmd( IConsoleCmdArgs* pArgs )
{
	ICryLobbyService*				pLobbyService;
	ICryFriendsManagement*	pFriendsManagement;

	pLobbyService = gEnv->pNetwork->GetLobby()->GetLobbyService( eCLS_Online );
	pFriendsManagement = pLobbyService->GetFriendsManagement();

	if ( pFriendsManagement )
	{
		const char*						pArg = NULL;
		char*									pEnd;

		if ( pArgs && ( pArgs->GetArgCount() == 2 ) )
		{
			pArg = pArgs->GetArg( 1 );
		}

		if ( pArg )
		{
			GPProfile							profileID = strtoul( pArg, &pEnd, 10 );

			if ( ( pEnd > pArg ) && !*pEnd )
			{
				SCryGameSpyUserID*		pGameSpyUserID = new SCryGameSpyUserID( profileID );

				if ( pGameSpyUserID )
				{
					CryUserID							pUserID = pGameSpyUserID;
					ECryLobbyError				error = pFriendsManagement->FriendsManagementAcceptFriendRequest( 0, &pUserID, 1, NULL, NULL, NULL );
					
					if ( error == eCLE_Success )
					{
						NetLogAlways( "[GameSpy] Accepted friend request from profile ID %u", profileID );
					}
					else
					{
						NetLogAlways( "[GameSpy] Error accepting friend request from profile ID %u: error code %u", profileID, error );
					}
				}
				else
				{
					NetLogAlways( "[GameSpy] Error: out of memory" );
				}
			}
			else
			{
				NetLogAlways( "[GameSpy] Error: argument must be a GameSpy profile ID" );
			}
		}
		else
		{
			NetLogAlways( "[GameSpy] Error: command requires exactly one argument" );
		}
	}
	else
	{
		NetLogAlways( "[GameSpy] Error: no friends management" );
	}
}


void CNetCVars::GameSpyRevokeFriendStatusCmd( IConsoleCmdArgs* pArgs )
{
	ICryLobbyService*				pLobbyService;
	ICryFriendsManagement*	pFriendsManagement;

	pLobbyService = gEnv->pNetwork->GetLobby()->GetLobbyService( eCLS_Online );
	pFriendsManagement = pLobbyService->GetFriendsManagement();

	if ( pFriendsManagement )
	{
		const char*						pArg = NULL;
		char*									pEnd;

		if ( pArgs && ( pArgs->GetArgCount() == 2 ) )
		{
			pArg = pArgs->GetArg( 1 );
		}

		if ( pArg )
		{
			GPProfile							profileID = strtoul( pArg, &pEnd, 10 );

			if ( ( pEnd > pArg ) && !*pEnd )
			{
				SCryGameSpyUserID*		pGameSpyUserID = new SCryGameSpyUserID( profileID );

				if ( pGameSpyUserID )
				{
					CryUserID							pUserID = pGameSpyUserID;
					ECryLobbyError				error = pFriendsManagement->FriendsManagementRevokeFriendStatus( 0, &pUserID, 1, NULL, NULL, NULL );

					if ( error == eCLE_Success )
					{
						NetLogAlways( "[GameSpy] Revoked friend status for profile ID %u", profileID );
					}
					else
					{
						NetLogAlways( "[GameSpy] Error revoking friend status for profile ID %u: error code %u", profileID, error );
					}
				}
				else
				{
					NetLogAlways( "[GameSpy] Error: out of memory" );
				}
			}
			else
			{
				NetLogAlways( "[GameSpy] Error: argument must be a GameSpy profile ID" );
			}
		}
		else
		{
			NetLogAlways( "[GameSpy] Error: command requires exactly one argument" );
		}
	}
	else
	{
		NetLogAlways( "[GameSpy] Error: no friends management" );
	}
}


void CNetCVars::GameSpyLogFriendEventsCmd( IConsoleCmdArgs* pArgs )
{
	gEnv->pNetwork->GetLobby()->RegisterEventInterest( eCLSE_FriendRequest, GameSpyLogFriendEventCallback, "request" );
	gEnv->pNetwork->GetLobby()->RegisterEventInterest( eCLSE_FriendAuthorised, GameSpyLogFriendEventCallback, "authorised" );
	gEnv->pNetwork->GetLobby()->RegisterEventInterest( eCLSE_FriendRevoked, GameSpyLogFriendEventCallback, "revoked" );
	NetLogAlways( "[GameSpy] Now logging friend events" );
}


void CNetCVars::GameSpyLogFriendEventCallback( UCryLobbyEventData eventData, void *pUserParam )
{
	const SCryGameSpyUserID*	pSenderGameSpyUserID = static_cast< const SCryGameSpyUserID* >( eventData.pFriendIDData->m_user.get() );
	const char*					pEvent = static_cast< const char* >( pUserParam );
	
	NetLogAlways( "[GameSpy] Friend %s from user %d", pUserParam, pSenderGameSpyUserID->m_profileID );
}

void CNetCVars::GameSpyBlockProfile(IConsoleCmdArgs* pArgs)
{
	ICryFriendsManagement* pFriendsManagement = NULL;
	INetwork* pNetwork = gEnv->pNetwork;
	if (pNetwork != NULL)
	{
		ICryLobby* pLobby = pNetwork->GetLobby();
		if (pLobby != NULL)
		{
			ICryLobbyService* pLobbyService = pLobby->GetLobbyService(eCLS_Online);
			if (pLobbyService != NULL)
			{
				pFriendsManagement = pLobbyService->GetFriendsManagement();
			}
		}
	}

	if (pFriendsManagement != NULL)
	{
		if ((pArgs != NULL) && (pArgs->GetArgCount() == 2))
		{
			const char* pArg = pArgs->GetArg(1);
			char* pEnd;
			GPProfile profileID = strtoul(pArg, &pEnd, 10);

			if ((pEnd > pArg) && (!*pEnd))
			{
				SCryGameSpyUserID* pGameSpyUserID = new SCryGameSpyUserID(profileID);

				if (pGameSpyUserID != NULL)
				{
					CryUserID pUserID = pGameSpyUserID;
					ECryLobbyError error = pFriendsManagement->FriendsManagementBlockUser(0, &pUserID, 1, NULL, NULL, NULL);

					if (error == eCLE_Success)
					{
						NetLogAlways("[GameSpy] Blocked profile ID %u", profileID);
					}
					else
					{
						NetLogAlways("[GameSpy] Error blocking profile ID %u: error code %u", profileID, error);
					}
				}
				else
				{
					NetLogAlways("[GameSpy] Error: out of memory");
				}
			}
		}
		else
		{
			NetLogAlways("[GameSpy] Error: command requires exactly one argument");
		}
	}
}

void CNetCVars::GameSpyUnblockProfile(IConsoleCmdArgs* pArgs)
{
	ICryFriendsManagement* pFriendsManagement = NULL;
	INetwork* pNetwork = gEnv->pNetwork;
	if (pNetwork != NULL)
	{
		ICryLobby* pLobby = pNetwork->GetLobby();
		if (pLobby != NULL)
		{
			ICryLobbyService* pLobbyService = pLobby->GetLobbyService(eCLS_Online);
			if (pLobbyService != NULL)
			{
				pFriendsManagement = pLobbyService->GetFriendsManagement();
			}
		}
	}

	if (pFriendsManagement != NULL)
	{
		if ((pArgs != NULL) && (pArgs->GetArgCount() == 2))
		{
			const char* pArg = pArgs->GetArg(1);
			char* pEnd;
			GPProfile profileID = strtoul(pArg, &pEnd, 10);

			if ((pEnd > pArg) && (!*pEnd))
			{
				SCryGameSpyUserID* pGameSpyUserID = new SCryGameSpyUserID(profileID);

				if (pGameSpyUserID != NULL)
				{
					CryUserID pUserID = pGameSpyUserID;
					ECryLobbyError error = pFriendsManagement->FriendsManagementUnblockUser(0, &pUserID, 1, NULL, NULL, NULL);

					if (error == eCLE_Success)
					{
						NetLogAlways("[GameSpy] Unblocked profile ID %u", profileID);
					}
					else
					{
						NetLogAlways("[GameSpy] Error unblocking profile ID %u: error code %u", profileID, error);
					}
				}
				else
				{
					NetLogAlways("[GameSpy] Error: out of memory");
				}
			}
		}
		else
		{
			NetLogAlways("[GameSpy] Error: command requires exactly one argument");
		}
	}
}

void CNetCVars::GameSpyIsProfileBlocked(IConsoleCmdArgs* pArgs)
{
	ICryFriendsManagement* pFriendsManagement = NULL;
	INetwork* pNetwork = gEnv->pNetwork;
	if (pNetwork != NULL)
	{
		ICryLobby* pLobby = pNetwork->GetLobby();
		if (pLobby != NULL)
		{
			ICryLobbyService* pLobbyService = pLobby->GetLobbyService(eCLS_Online);
			if (pLobbyService != NULL)
			{
				pFriendsManagement = pLobbyService->GetFriendsManagement();
			}
		}
	}

	if (pFriendsManagement != NULL)
	{
		if ((pArgs != NULL) && (pArgs->GetArgCount() == 2))
		{
			const char* pArg = pArgs->GetArg(1);
			char* pEnd;
			GPProfile profileID = strtoul(pArg, &pEnd, 10);

			if ((pEnd > pArg) && (!*pEnd))
			{
				SCryGameSpyUserID* pGameSpyUserID = new SCryGameSpyUserID(profileID);

				if (pGameSpyUserID != NULL)
				{
					CryUserID pUserID = pGameSpyUserID;
					ECryLobbyError error = pFriendsManagement->FriendsManagementIsUserBlocked(0, &pUserID, 1, NULL, GameSpyIsProfileBlockedCallback, NULL);

					if (error != eCLE_Success)
					{
						NetLogAlways("[GameSpy] Error checking profile ID %u: error code %u", profileID, error);
					}
				}
				else
				{
					NetLogAlways("[GameSpy] Error: out of memory");
				}
			}
		}
		else
		{
			NetLogAlways("[GameSpy] Error: command requires exactly one argument");
		}
	}
}


void CNetCVars::GameSpyIsProfileBlockedCallback(CryLobbyTaskID taskID, ECryLobbyError error, SFriendManagementInfo* pInfo, uint32 numUserIDs, void* pArg)
{
	for (uint32 index = 0; index < numUserIDs; ++index)
	{
		const SCryGameSpyUserID* pUserID = static_cast<const SCryGameSpyUserID*>(pInfo[index].userID.get());
		NetLogAlways( "[GameSpy] %u: is%s blocked", pUserID->m_profileID, (pInfo[index].result) ? "" : " not");
	}
}

void CNetCVars::GameSpyIsUserFriend( IConsoleCmdArgs* pArgs )
{
	ICryFriendsManagement* pFriendsManagement = NULL;
	INetwork* pNetwork = gEnv->pNetwork;
	if (pNetwork != NULL)
	{
		ICryLobby* pLobby = pNetwork->GetLobby();
		if (pLobby != NULL)
		{
			ICryLobbyService* pLobbyService = pLobby->GetLobbyService(eCLS_Online);
			if (pLobbyService != NULL)
			{
				pFriendsManagement = pLobbyService->GetFriendsManagement();
			}
		}
	}

	if (pFriendsManagement != NULL)
	{
		if ((pArgs != NULL) && (pArgs->GetArgCount() == 2))
		{
			const char* pArg = pArgs->GetArg(1);
			char* pEnd;
			GPProfile profileID = strtoul(pArg, &pEnd, 10);

			if ((pEnd > pArg) && (!*pEnd))
			{
				SCryGameSpyUserID* pGameSpyUserID = new SCryGameSpyUserID(profileID);

				if (pGameSpyUserID != NULL)
				{
					CryUserID pUserID = pGameSpyUserID;
					ECryLobbyError error = pFriendsManagement->FriendsManagementIsUserFriend( 0, &pUserID, 1, NULL, GameSpyIsUserFriendCallback, NULL );

					if (error != eCLE_Success)
					{
						NetLogAlways("[GameSpy] Error checking profile ID %u: error code %u", profileID, error);
					}
				}
				else
				{
					NetLogAlways("[GameSpy] Error: out of memory");
				}
			}
			else
			{
				NetLogAlways("[GameSpy] Error: argument must be a numeric profile ID");
			}
		}
		else
		{
			NetLogAlways("[GameSpy] Error: command requires exactly one argument");
		}
	}
}


void CNetCVars::GameSpyIsUserFriendCallback( CryLobbyTaskID taskID, ECryLobbyError error, SFriendManagementInfo* pInfo, uint32 numUserIDs, void* pArg )
{
	for (uint32 index = 0; index < numUserIDs; ++index)
	{
		const SCryGameSpyUserID* pUserID = static_cast<const SCryGameSpyUserID*>(pInfo[index].userID.get());
		NetLogAlways( "[GameSpy] %u: is%s a friend", pUserID->m_profileID, (pInfo[index].result) ? "" : " not");
	}
}

void CNetCVars::GameSpyGetName( IConsoleCmdArgs* pArgs )
{
	ICryFriendsManagement* pFriendsManagement = NULL;
	INetwork* pNetwork = gEnv->pNetwork;
	if (pNetwork != NULL)
	{
		ICryLobby* pLobby = pNetwork->GetLobby();
		if (pLobby != NULL)
		{
			ICryLobbyService* pLobbyService = pLobby->GetLobbyService(eCLS_Online);
			if (pLobbyService != NULL)
			{
				pFriendsManagement = pLobbyService->GetFriendsManagement();
			}
		}
	}

	if (pFriendsManagement != NULL)
	{
		if ((pArgs != NULL) && (pArgs->GetArgCount() == 2))
		{
			const char* pArg = pArgs->GetArg(1);
			char* pEnd;
			GPProfile profileID = strtoul(pArg, &pEnd, 10);

			if ((pEnd > pArg) && (!*pEnd))
			{
				SCryGameSpyUserID* pGameSpyUserID = new SCryGameSpyUserID(profileID);

				if (pGameSpyUserID != NULL)
				{
					CryUserID pUserID = pGameSpyUserID;
					ECryLobbyError error = pFriendsManagement->FriendsManagementGetName( 0, &pUserID, 1, NULL, GameSpyGetNameCallback, NULL );

					if (error != eCLE_Success)
					{
						NetLogAlways("[GameSpy] Error checking profile ID %u: error code %u", profileID, error);
					}
				}
				else
				{
					NetLogAlways("[GameSpy] Error: out of memory");
				}
			}
			else
			{
				NetLogAlways("[GameSpy] Error: argument must be a numeric profile ID");
			}
		}
		else
		{
			NetLogAlways("[GameSpy] Error: command requires exactly one argument");
		}
	}
}

void CNetCVars::GameSpyGetNameCallback( CryLobbyTaskID taskID, ECryLobbyError error, SFriendInfo* pInfo, uint32 numUserIDs, void* pCbArg )
{
	for (uint32 index = 0; index < numUserIDs; ++index)
	{
		const SCryGameSpyUserID* pUserID = static_cast<const SCryGameSpyUserID*>(pInfo[index].userID.get());
		NetLogAlways( "[GameSpy] %u: %s", pUserID->m_profileID, pInfo[index].name );
	}
}

void CNetCVars::GameSpyFindUser( IConsoleCmdArgs* pArgs )
{
	ICryFriendsManagement* pFriendsManagement = NULL;
	INetwork* pNetwork = gEnv->pNetwork;
	if (pNetwork != NULL)
	{
		ICryLobby* pLobby = pNetwork->GetLobby();
		if (pLobby != NULL)
		{
			ICryLobbyService* pLobbyService = pLobby->GetLobbyService(eCLS_Online);
			if (pLobbyService != NULL)
			{
				pFriendsManagement = pLobbyService->GetFriendsManagement();
			}
		}
	}

	if (pFriendsManagement != NULL)
	{
		if ((pArgs != NULL) && (pArgs->GetArgCount() == 2))
		{
			const char* pArg = pArgs->GetArg(1);
			SFriendManagementSearchInfo	info;

			if ( strlen( pArg ) < sizeof ( info.name ) )
			{
				strcpy( info.name, pArg );
				ECryLobbyError error = pFriendsManagement->FriendsManagementFindUser( 0, &info, 1, NULL, GameSpyFindUserCallback, NULL );

				if (error != eCLE_Success)
				{
					NetLogAlways("[GameSpy] Error checking name %s: error code %u", pArg, error);
				}
			}
			else
			{
				NetLogAlways("[GameSpy] Error: argument must be a string no longer than %d characters", sizeof ( info.name ) - 1 );
			}
		}
		else
		{
			NetLogAlways("[GameSpy] Error: command requires exactly one argument");
		}
	}
}

void CNetCVars::GameSpyFindUserCallback( CryLobbyTaskID taskID, ECryLobbyError error, SFriendInfo* pInfo, uint32 numUserIDs, void* pCbArg )
{
	for (uint32 index = 0; index < numUserIDs; ++index)
	{
		const SCryGameSpyUserID* pUserID = static_cast<const SCryGameSpyUserID*>(pInfo[index].userID.get());
		NetLogAlways( "[GameSpy] %s: %u", pInfo[index].name, pUserID->m_profileID );
	}
}

#endif
